package com.kstech.ks_gaz01.socket

import android.content.Context
import com.blankii.androidlibrary.exts.logd
import com.blankii.androidlibrary.exts.loge
import com.blankii.androidlibrary.exts.logw
import com.blankii.androidlibrary.utils.CRC16Util
import com.blankii.androidlibrary.utils.CalcUtil
import com.easysocket.EasySocket
import com.easysocket.config.EasySocketOptions
import com.easysocket.entity.OriginReadData
import com.easysocket.entity.SocketAddress
import com.easysocket.interfaces.conn.SocketActionListener
import com.kstech.ks_gaz01.XML_ADDRESS_WRITE_START_WORK
import com.kstech.ks_gaz01.XML_GROUP_ADDRESS_FAULT
import com.kstech.ks_gaz01.XML_REV_FAULT
import com.kstech.ks_gaz01.XML_VALUE_WRITE_START_WORK
import com.kstech.ks_gaz01.XML_XS_GZXX
import com.kstech.ks_gaz01.base.HomeViewModel
import com.kstech.ks_gaz01.data.DebugDataBean
import com.kstech.ks_gaz01.gz_size
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.LinkedBlockingQueue

/**
 * author: lenovo
 * created on: 2023/12/19 16:09
 * description:
 * ******************** 程序消息队列运行规则 ***************************
 *
 * 平板由03功能码队列，与消息队列和接收链表组成
 * 03功能码队列：主线程中把要读的列表加入其中，再使用子线程加入到消息队列中
 * 消息队列：每隔100ms 发送并弹出队头数据
 * 接收链表：接收到控制器的数据所有数据，当发送一个表的个数等于接收到到个数时，再一起更新UI界面
 *
 *********************** 常用队列方法 **************************

 * peek（）获取队头元素，容量为0时，返回null
 * poll() 弹出元素(删除）容量为0时， 返回false
 * offer() 队尾加入元素，超出容量时，返回false
 * take() 获取队头元素，并且删除该元素
 *
 * ******************** modbus 功能码说明 ***************************
 *
 * 功能码-03 读取
 * 发送：01 03 ff ff 00 01 CRC CRC
 * 接收：01 03 02 00 01 00 01 CRC CRC   ========字节数21个，结尾带空格
 * 功能码-06 写入
 * 发送：01 06 ff ff 00 01 CRC CRC
 * 接收：01 06 ff ff 00 01 CRC CRC    ====与发送一样
 * substring(3, 5) 功能码
 * substring(6,11) 地址
 * substring(12, 17) 结果数据
 *
 * ********************** 业务逻辑 *************************
 *
 * 读取-返回：地址、结果字符串
 * 写入-返回：地址、
 * 接收：-------------
 * HomeFragment-故障地址（循环）
 * Config: 读一次的itemAddress
 * RealTime: 循环读的itemAddress 和 循环读的车辆位姿
 * SensorSetting:循环读的itemAddress 或者多个单address
 *
 *
 * 读取-
 *  地址：
 *      itemAddress请求组数据（返回N个字节）
 *      item_address 单个数据（返回两个字节）
 *  数据类型：
 *      常驻数据：车辆位姿、故障信息(都是循环的) 加入的界面不同
 *      切换数据：通过点击加载的数据（分两种）
 *  是否循环：数据分为请求一次数据、循环请求数据两种
 *
 *
 * 写入-
 *  地址
 *  值

 * 优先级概念：
 * 1.切换数据优先级>常驻数据，每一次切换变换时，都需要让行切换数据
 * 2.写入优先读取数据
 *
 * todo 目前一个界面 必须都是读一次的 或者是读多次的
 */
object SocketManagerNew : SocketActionListener() {
    private const val TAG: String = "SocketManagerNew"

    /**
     * 消息队列
     */
    private val msgQueue = LinkedBlockingQueue<String>()//读取-发送-消息队列
    private val msgWriteQueue = LinkedBlockingQueue<String>()//写入-发送-消息队列
    private val readyLooperList = CopyOnWriteArrayList<ReadBean>()//缓存-消息队列

    /**
     * 监听器
     */
    private var socketConnectStatusListener: SocketConnectionStatusListener? = null
    private var socketFragmentListener: SocketFragmentListener? = null

    /**
     * 是否循环
     */
    private var isLooper = false

    /**
     * 接收超时
     */
    private var revTimeoutTime: Long = 0

    /**
     * 当前工作状态
     */
    private var workState: WorkState = WorkState.NOT_CONNECT

    /**
     * 发送的当前address
     */
    private lateinit var mReadBean: ReadBean

    /**
     * 当前缓存队列读取到的消息位
     */
    private var sendPosition = 0

    /**
     * 消息队列遍历时间间隔（发送频率）
     */
    private const val DELAY_TIME: Long = 60

    /**
     * 超时判定，超过这个时间未收到回复，则重新发送
     */
    private const val MIN_TIMEOUT_TIME = 7000

    /**
     * 平板在线  43709-12345
     * 软件打开并联机后发送，如未收到控制器反馈，则继续写入，直到有反馈为止
     * true:与控制通讯成功
     * false：未与控制器连接
     */
    private var revStartTag = false

    /**
     * 发送故障码频率,比如到统计到40次了发送一次故障码查询
     */
    private var sendFaultCount = 0

    /**
     * 判断03查询指令是故障码查询还是其他
     * true：故障码
     * false：其他
     */
    private var systemSendFault = false

    /**
     * 发送故障码指令开关
     * 车型是否发送故障码，有的车型没有故障信息，所以不能发送，发送的后会有报错，根据XML配置判断
     * 在导出文件中,导入文件中不发送故障码
     * true:满足发送故障码条件
     * false：不满足
     * todo 优化一下，在读取、写入excel 的界面的时候停止故障信息的请求 如果有故障信息的话
     */
    var startSendFault = true

    /**
     * thread生命周期
     */
    var threadLive = true


    /**
     * 开启消息线程
     */
    private fun startSendTask() {
        CoroutineScope(Dispatchers.IO).launch {
            while (threadLive) {
                if (workState == WorkState.NOT_CONNECT) {
                    continue
                }
                if (revStartTag) {
                    processQueue()
                } else {
                    processStartTag()
                }
                delay(DELAY_TIME)
                if (revTimeoutTime < MIN_TIMEOUT_TIME) {
                    revTimeoutTime += DELAY_TIME
                } else {
                    if (workState == WorkState.SENDING) {
                        //任务未完成，进入超时期
                        changeWorkState(WorkState.TIMEOUT)
                    }
                }
            }
        }
    }

    private fun clearQueue() {
        readyLooperList.clear()
    }

    /**
     * 清空可读队列
     */
    public fun clearReadList() {
        readyLooperList.clear()
    }

    /**
     * 清空所有消息队列信息
     */
    public fun closeThread() {
        threadLive = false
        revStartTag = false
        msgQueue.clear()
        msgWriteQueue.clear()
        readyLooperList.clear()
    }

    /**
     * 启动队列
     */
    public fun startThread() {
        threadLive = true
    }

    /**
     * 循环发送，直到收到反馈为止
     */
    private fun processStartTag() {
        sendWriteMsg(XML_ADDRESS_WRITE_START_WORK, XML_VALUE_WRITE_START_WORK)
        sendOneMsg(msgWriteQueue.take())
    }

    /**
     * WorkState.READY:装载一组预备数据，发送第一个
     * WorkState.FINISHED:收到一组数据的响应，发送下一个
     * WorkState.TIMEOUT:因超时重新需要重新发送，重新发这一个
     */
    private suspend fun processQueue() {
        //todo 优化
        if (workState == WorkState.FAULT_FINISHED || workState == WorkState.RESTART || workState == WorkState.READY || workState == WorkState.FINISHED || workState == WorkState.TIMEOUT) {
            /**
             * 发送读取消息队列
             * 读：发送故障码 与 发送预备数据消息 二选一
             */
            if (startSendFault && sendFaultCount > 40) {//发送30条以上再查一遍故障码
                /**
                 * 发送故障码
                 */
                /**
                 * todo  socket优化
                 * 大一 这里需要重新理解一遍优化
                 * mReadBean = ReadBean(XML_GROUP_ADDRESS_FAULT, XML_REV_FAULT, 2)
                 * sendReadMsg(XML_GROUP_ADDRESS_FAULT, "3")
                 */
                mReadBean = ReadBean(XML_GROUP_ADDRESS_FAULT, XML_REV_FAULT, gz_size)
                sendReadMsg(XML_GROUP_ADDRESS_FAULT, "${gz_size + 1}")
                systemSendFault = true
            } else {
                /**
                 * 通过sendPosition 实现超时重发
                 */
                if (readyLooperList.size > 0 && msgQueue.size == 0 && sendPosition < readyLooperList.size) {
                    mReadBean = readyLooperList[sendPosition]
                    sendReadMsg(mReadBean.address, mReadBean.itemSize.toString())
                }
            }
            if (msgQueue.size > 0) {
                changeWorkState(WorkState.SENDING)
                sendOneMsg(msgQueue.take())
            }
            sendFaultCount++
        }
        /**
         * 发送写入消息队列
         */
        if (msgWriteQueue.size > 0) {
            delay(DELAY_TIME)
            sendOneMsg(msgWriteQueue.take())
        }
    }

    /**
     * 切换状态
     */
    private fun changeWorkState(state: WorkState) {
        if (workState != state) {
            workState = state
            when (state) {
                WorkState.RESTART -> {

                }

                WorkState.IDLE_TIME -> {

                }

                WorkState.FAULT_FINISHED -> {
                    /**
                     * 只有接收故障码成功之后才会清零
                     * 否则判定为接收超时，重新发送故障码
                     */
                    sendFaultCount = 0
                }

                WorkState.NOT_CONNECT -> {
                    revStartTag = false
                }

                WorkState.READY -> {
                    revTimeoutTime = 0
                }

                WorkState.SENDING -> {
                    revTimeoutTime = 0
                }

                WorkState.FINISHED -> {
                    revTimeoutTime = 0
                    sendPosition++
                    if (isLooper && sendPosition >= readyLooperList.size) {
                        sendPosition = 0
                    }
                    "WorkState.FINISHED.sendPosition:${sendPosition}".logd(TAG)
                }

                WorkState.TIMEOUT -> {
                    //如果不是循环发送的数列，当发生超时情况时，表明数据接收异常，给其重新发送一次的权利
                    "超时重新发送一遍$revTimeoutTime,sendPosition:${sendPosition}".logd(TAG)
                }
            }
        }
    }

    /**
     * 接收数据
     */
    override fun onSocketResponse(socketAddress: SocketAddress?, originReadData: OriginReadData?) {
        super.onSocketResponse(socketAddress, originReadData)
        /**
         * 源数据
         */
        val hexStr = CalcUtil.Byte2Hex(originReadData!!.originDataBytes)
        mViewModel.logData.postValue("接收消息：$hexStr\n")
        /**
         * 分辨功能码
         */
        when (hexStr.substring(3, 5)) {
            "03" -> {
                /**
                 * 对于脏数据的处理
                 * 1 只有程序进入发送阶段时收到的数据才能接收
                 * 2 判定接收到的数据个数是否与发送的数据个数一致
                 * 3 添加延迟，切换数据时延时
                 */
                if (workState != WorkState.SENDING) {
                    return
                }
                /**
                 * 比较发送真个数与接收真个数
                 */
                val bytes = hexStr.substring(6, 8).toInt(16)
                "接收03数据：$hexStr,字节个数${bytes}".logd(TAG)
                if (8 + 3 * bytes >= hexStr.length) {
                    socketConnectStatusListener?.onSocketMsg("接收的数据格式异常")
                    return
                }
                var revCount = 0
                var revArray = ArrayList<String>()
                var revIntAddress = ""//十进制
                if (mReadBean.revAddress.isNotEmpty()) {
                    /**
                     * 使用GroupAddress请求的数据，多接收两个字节的groupAddress,显示数据应该删除掉
                     */
                    revIntAddress = CalcUtil.Hex2IntStr(hexStr.substring(9, 14))
                    if (mReadBean.revAddress != revIntAddress) {
//                        socketConnectStatusListener?.showSocketMsg("接收的组包地址异常")
                        mViewModel.logData.postValue("异常：-------应收返回值:${mReadBean.revAddress} 实际收到值:${revIntAddress}-------\n")
                        "-------接收的组包地址异常 发送地址:${mReadBean.revAddress} 接收地址:${revIntAddress}-------".loge(
                            TAG
                        )
                        return
                    }
                    revArray.addAll(hexStr.substring(15, 8 + 3 * bytes).split(" "))
                    revCount = revArray.size / 2 + 1
                } else {
                    /**
                     * 不是用GroupAddress请求的数据，接收的数据
                     */
                    revArray.addAll(hexStr.substring(9, 8 + 3 * bytes).split(" "))
                    revCount = revArray.size / 2
                }

                if (revCount != mReadBean.itemSize) {
                    // socketConnectStatusListener?.showSocketMsg("接收的数据位异常，接收数据个数${revCount}, 需要更新数据个数${mReadBean.itemSize}")
                    return
                }
                val resultArray = ArrayList<String>()
                for (i in 0 until revArray.size / 2) {
                    val result = revArray[2 * i] + revArray[2 * i + 1]
                    resultArray.add(result)
                }
                /**
                 * 故障码全给拦截走了，实时参数没有了
                 */
                changeWorkState(WorkState.FINISHED)

                if (XML_REV_FAULT == revIntAddress && systemSendFault) {
                    systemSendFault = false
                    socketConnectStatusListener?.onSocketFaultMsg(resultArray)
                    changeWorkState(WorkState.FAULT_FINISHED)
                } else {
                    socketFragmentListener?.readSuccess(
                        mReadBean.address, resultArray
                    )
                }
            }

            "06" -> {
                "接收06数据：$hexStr".logd(TAG)
                if (hexStr.length < 18) {//防止有脏数据，避免string.substring 导致崩溃
                    return
                }
                val revAddress = hexStr.substring(6, 11)
                val revValue = hexStr.substring(12, 17)
                val intStr = CalcUtil.Hex2IntStr(revAddress)
                /**
                 * 判定与控制器连接成功
                 */
                if (!revStartTag && "4$intStr" == XML_ADDRESS_WRITE_START_WORK) {
                    revStartTag = true
                    sendFaultCount = 0
                } else {
                    socketFragmentListener?.writeSuccess(
                        intStr, revValue
                    )
                }
            }
        }
    }


    /**
     * 用来验证收到的数据位格式对不对
     * itemSize 输入一个数字 revAddress=“” 输出自动加一
     * itemSize 输入一个数字 revAddress！=“” 输出=输出
     */
    class ReadBean(
        val address: String, val revAddress: String = "", _itemSize: Int = 1
    ) {
        var itemSize = _itemSize
            get() = if (revAddress.isNotEmpty()) {
                field + 1
            } else {
                field
            }
    }

    /**
     * @param isLooper true=循环 false=只发一次
     * @param readBeans 地址数组
     */
    fun addReadyLooperList(isLooper: Boolean, readBeans: ArrayList<ReadBean>) {
        if (workState != WorkState.NOT_CONNECT) {
            changeWorkState(WorkState.RESTART)
        }
        readyLooperList.clear()
        sendPosition = 0
        revTimeoutTime = 0
        this.isLooper = isLooper
        readyLooperList.addAll(readBeans)
        readyLooperList.forEach {
            "切换数据:${it.address},${it.itemSize}".logd(TAG)
        }
    }

    private fun sendOneMsg(hex: String) {
        mViewModel.logData.postValue("发送消息：${hex}\n")
        "弹出队列：${hex}".logd(TAG)
        EasySocket.getInstance().upMessage(CalcUtil.Hex2Byte(hex))
    }

    private lateinit var mViewModel: HomeViewModel

    fun initViewModel(viewModel: HomeViewModel) {
        mViewModel = viewModel
    }

    /**
     * 初始化EasySocket
     */
    fun initEasySocket(context: Context, ip: String, port: Int) {
        //socket配置
        var options = EasySocketOptions.Builder().setSocketAddress(SocketAddress(ip, port)) //主机地址
            // 强烈建议定义一个消息协议，方便解决 socket黏包、分包的问题
//            .setReaderProtocol(DefaultMessageProtocol()) // 默认的消息协议
            .build()
        //初始化EasySocket
        EasySocket.getInstance().createConnection(options, context)
        //绑定事件
        EasySocket.getInstance().subscribeSocketAction(this)
        //开始自动发送队列
        startThread()
        startSendTask()
        //检查XML 是否发送故障信息
        checkXMLisSendError()
    }

    private fun checkXMLisSendError() {
        val isSendGZ = DebugDataBean.xmlSettings.contains(XML_XS_GZXX)
        if (!isSendGZ) {
            "请检查该车型XML文件 XmlSetting中是否存在 是否发送故障信息".logw(TAG)
        } else if (!DebugDataBean.xmlSettings[XML_XS_GZXX]!!) {
            startSendFault = false;
        } else {
            startSendFault = true;
        }
    }

    /**
     * 发送写入数据
     * 当 输入-1  但是单位写的是正的 会出现写入卡住的情况
     * 平板 一直写入 01 06 0e a5 ff ff ff ff ab 64 控制器不会应答
     * todo 优化一下，提示卡住的原因
     */
    fun sendWriteMsg(
        address: String, floatStr: String = "1", isNegativeNumber: Boolean = false
    ): String {
        return addMsgQueue(false, address, floatStr, isNegativeNumber)
    }

    fun sendReadMsg(
        address: String, floatStr: String = "1"
    ) {
        addMsgQueue(true, address, floatStr)
    }

    /**
     * @param reqModel Boolean true 代表读取功能码03 ，false 代表写入功能码06
     * @param address  String 必须是4开头的 如41111
     * @param floatStr String 写入的数据值  十进制  读取时默认为 1
     * @param isNegativeNumber Boolean  写入数据要带是否是负号
     * @param isFault Boolean  获取返回十六进制
     */
    private fun addMsgQueue(
        reqModel: Boolean,
        address: String,
        floatStr: String = "1",
        isNegativeNumber: Boolean = false,
        isFault: Boolean = false
    ): String {
        val resultMsg = StringBuilder()
        var addressInt = 0
        resultMsg.append("01 ")
        //读取请求true   还是输入请求false
        if (address.length != 5) {
            throw Exception("socket address 长度非法")
        } else {
            addressInt = address.substring(1).toInt()
        }
        addressInt = if (reqModel) {
            resultMsg.append("03 ")//功能码读取一个参数
            addressInt // 读取值，地址不减一
        } else {
            resultMsg.append("06 ")//功能码写入一个参数
            addressInt
        }
        if (isNegativeNumber) {// 写入的时候注意正负值
            //加入地址 两个字节十六进制
            resultMsg.append(CalcUtil.Int2SpaceHex(addressInt, false))
            resultMsg.append(" ")//加空格
            //加入数据 两个字节十六进制
            resultMsg.append(CalcUtil.Int2SpaceHex(floatStr.toFloat().toInt(), false))
            resultMsg.append(" ")//加空格
        } else {
            //加入地址 两个字节十六进制
            resultMsg.append(CalcUtil.Int2SpaceHex(addressInt, true))
            resultMsg.append(" ")//加空格
            //加入数据 两个字节十六进制
            resultMsg.append(CalcUtil.Int2SpaceHex(floatStr.toFloat().toInt(), true))
            resultMsg.append(" ")//加空格
        }
        //生成CRC16 校验码
        val crc16: String = CRC16Util.getCRC((CalcUtil.Hex2Byte(resultMsg.toString().trim())))
        //翻转CRC16 校验码
        resultMsg.append(CalcUtil.processCRC16(crc16))
        if (isFault) {
            return resultMsg.toString()
        } else {
            //加入队列
            if (reqModel) {
                msgQueue.put(resultMsg.toString())
            } else {
                msgWriteQueue.put(resultMsg.toString())
            }
        }
        return resultMsg.toString()
    }


    //fragment 监听socket相关行为
    fun subscribeSocketFragmentListener(
        socketFragmentListener: SocketFragmentListener,
    ) {
        this.socketFragmentListener = socketFragmentListener
    }


    fun unSubscribeSocketFragmentListener() {
        this.socketFragmentListener = null
        clearQueue()
    }

    //activity 监听socket相关行为
    fun subscribeSocketConnectionListener(socketConnectionStatusListener: SocketConnectionStatusListener) {
        this.socketConnectStatusListener = socketConnectionStatusListener
    }

    //activity 监听socket相关行为
    fun unSubscribeSocketConnectionListener() {
        this.socketConnectStatusListener = null
    }

    override fun onSocketConnFail(socketAddress: SocketAddress?, isNeedReconnect: Boolean) {
        super.onSocketConnFail(socketAddress, isNeedReconnect)
        socketConnectStatusListener?.onSocketConnected(SocketConnectedStatus.FAILED)
        changeWorkState(WorkState.NOT_CONNECT)
    }

    override fun onSocketConnSuccess(socketAddress: SocketAddress?) {
        super.onSocketConnSuccess(socketAddress)
        socketConnectStatusListener?.onSocketConnected(SocketConnectedStatus.SUCCESS)
        if (workState == WorkState.NOT_CONNECT) {
            changeWorkState(WorkState.READY)
        }
    }

    override fun onSocketDisconnect(socketAddress: SocketAddress?, isNeedReconnect: Boolean) {
        super.onSocketDisconnect(socketAddress, isNeedReconnect)
        socketConnectStatusListener?.onSocketConnected(SocketConnectedStatus.DISCONNECT)
        changeWorkState(WorkState.NOT_CONNECT)
    }
}